package com.allwees.bs.module.lms.service.impl;


import com.allwees.bs.module.lms.dao.LogisticsWaybillDao;
import com.allwees.bs.module.lms.entity.LogisticsWaybill;
import com.allwees.bs.module.lms.service.LogisticsTrackService;
import com.allwees.bs.module.lms.track.MileRequest;
import com.allwees.bs.module.lms.track.MileTrackParam;
import com.allwees.bs.module.lms.track.TrackingHelper;
import com.allwees.bs.module.lms.vo.*;
import com.allwees.core.common.util.DateUtil;
import com.allwees.core.common.util.json.JsonUtil;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 51Tracking物流查询接口
 */
@Service
@Slf4j
public class LogisticsTrackServiceImpl implements LogisticsTrackService {

    @Resource
    private LogisticsWaybillDao logisticsWaybillDao;

    @Value("${allwees.imile.customerId}")
    private String customerId;

    @Value("${allwees.imile.secretKey}")
    private String secretKey;

    @Override
    public void createPostOrder(String trackingNumber, String carrierCode) {
        TrackingHelper.subscribe51Tracking(trackingNumber,carrierCode);
        TrackingHelper.subscribeAfterShipTracking(trackingNumber,carrierCode);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public TrackDetailsDto syncTracking(String orderItemUuid) throws Exception {
        LogisticsWaybill entity = logisticsWaybillDao.findOneByField("order_item_uuid",orderItemUuid);
        if (entity == null || entity.getDelivered()) {
            return null;
        }

        String expressCode = entity.getExpressCode();
        String carrierCode = entity.getCarrierCode();
        if(StringUtils.isAnyBlank(expressCode,carrierCode)){
            return null;
        }
        boolean isAfterShip = true;
        TrackDetailsDto details ;
        if(carrierCode.equals("IMILE")) {
            details = this.trackingMileShipDetail(expressCode);
            if (details == null) {
                return null;
            }
            entity.setTrackingDetail(details.getMileJson());
        } else {
            details = this.trackingAfterShipDetail(expressCode, carrierCode);
            if (details == null) {
                details = this.tracking51Detail(expressCode, carrierCode);
                isAfterShip = false;
            }
            if (details == null) {
                return null;
            }
            if (isAfterShip) {
                String checkpointsJsonStr = JsonUtil.toJson(details.getData());
                entity.setTrackingDetail(convertTrackingDetail(checkpointsJsonStr));
            } else {
                entity.setTrackingDetail(JsonUtil.toJson(details.getData()));
            }
        }
        entity.setDelivered(details.getDelivered());
        entity.setDeliverDate(details.getDeliverDate());
        entity.setUpdatedAt(new Date());
        logisticsWaybillDao.updateByUuid(entity, entity.getUuid());
        return details;
    }
    private String convertTrackingDetail(String checkpointsJsonStr) {

        if(checkpointsJsonStr.contains("checkpoint_time")) {
            checkpointsJsonStr = checkpointsJsonStr.replace("checkpoint_time","Date");
        }
        if(checkpointsJsonStr.contains("location")) {
            checkpointsJsonStr = checkpointsJsonStr.replace("location","Details");
        }
        if(checkpointsJsonStr.contains("message")) {
            checkpointsJsonStr = checkpointsJsonStr.replace("message","StatusDescription");
        }
        if(checkpointsJsonStr.contains("subtag")) {
            checkpointsJsonStr = checkpointsJsonStr.replace("subtag","substatus");
        }
        if(checkpointsJsonStr.contains("tag")) {
            checkpointsJsonStr = checkpointsJsonStr.replace("tag","checkpoint_status");
        }
        return checkpointsJsonStr;
    }

    private TrackDetailsDto tracking51Detail(String trackingNumber, String carrierCode) throws Exception {
        TrackDetails trackInfo = TrackingHelper.get51TrackInfo(carrierCode, trackingNumber);
        if(trackInfo == null){
            return null;
        }

        TrackDetailsDto details = new TrackDetailsDto();
        TrackHeader meta = trackInfo.getMeta();
        details.setCode(meta.getCode());
        details.setMessage(meta.getMessage());

        TrackBody body = trackInfo.getData();
        details.setDeliverDate(body.getCreated_at());
        details.setDelivered(trackInfo.isDelivered());
        TrackInfoHolder originInfo = body.getOrigin_info();
        if(originInfo != null) {
            details.setData(originInfo.getTrackinfo());
        }
        return details;
    }

    private TrackDetailsDto trackingAfterShipDetail(String trackingNumber, String carrierCode) throws Exception {
        TrackAfterShipDetails trackAfterShipDetails = TrackingHelper.getTrackingByAfterShip(carrierCode, trackingNumber);
        if(trackAfterShipDetails == null){
            return null;
        }

        TrackDetailsDto details = new TrackDetailsDto();
        TrackHeader meta = trackAfterShipDetails.getMeta();
        details.setCode(meta.getCode());
        details.setMessage(meta.getMessage());

        TraceAfterShipResponseBody body = trackAfterShipDetails.getData();
        AfterShipBody afterShipBody = body.getTracking();
        details.setDelivered(afterShipBody.getTag().toLowerCase().contains("delivered"));
        if(afterShipBody.getShipment_delivery_date() != null) {
            details.setDeliverDate(DateUtil.parseDateWithZone(afterShipBody.getShipment_delivery_date()));
        }
        List<AfterShipCheckPoint> checkpoints = new ArrayList<>();
        for(AfterShipCheckPoint afterShipCheckPoint : afterShipBody.getCheckpoints()){
            if (afterShipCheckPoint.getCheckpoint_time() != null) {
                afterShipCheckPoint.setCheckTimeStamp(DateUtil.parseDateWithZone(afterShipCheckPoint.getCheckpoint_time()).getTime());
                afterShipCheckPoint.setCheckpoint_time(DateUtil.formatTime(DateUtil.parseDateWithZone(afterShipCheckPoint.getCheckpoint_time())));
            }
            Collections.sort(checkpoints); // 按checkpoint_time倒序
            checkpoints.add(afterShipCheckPoint);
        }
        details.setData(checkpoints);
        return details;
    }

    private static RateLimiter limiter = RateLimiter.create(0.5);
    /**
     * 该接口每次只能查询一个订单，只允许每2秒查询一次
     * @param trackingNumber
     * @return
     * @throws Exception
     */
    private TrackDetailsDto trackingMileShipDetail(String trackingNumber) throws Exception {
        limiter.acquire();
        MileTrackParam param = new MileTrackParam();
        param.setOrderNo(trackingNumber);
        MileRequest request = MileRequest.newTrackRequest(param,customerId,secretKey);
        MileTrackDetails trackMileShipDetails = TrackingHelper.getTrackingByMile(request);
        log.info("==============mile track response:{}",trackMileShipDetails);
        if(trackMileShipDetails == null){
            return null;
        }
        TrackDetailsDto details = new TrackDetailsDto();
        details.setCode(Integer.parseInt(trackMileShipDetails.getStatus()));
        details.setMessage(trackMileShipDetails.getDescription());

        List<MileResponseVo> locusDetailed = trackMileShipDetails.getLocusDetailed();
        String json = null;
        //locusDetailed里最多有一个对象
        for(MileResponseVo mileResponseVo : locusDetailed) {
            details.setDelivered(false);
            if(mileResponseVo.getLatestStatus().contains("delivery")) {
                details.setDelivered(true);
                details.setDeliverDate(DateUtil.parseTime(mileResponseVo.getLatestStatusTime()));
            }
            json = convertMile2TrackingDetail(JsonUtil.toJson(mileResponseVo.getLocus()));
        }
        details.setMileJson(json);
        return details;
    }
    private  String convertMile2TrackingDetail(String mileJsonStr) {

        if(mileJsonStr.contains("latestStatusTime")) {
            mileJsonStr = mileJsonStr.replace("latestStatusTime","Date");
        }

        if(mileJsonStr.contains("latestStatus")) {
            mileJsonStr = mileJsonStr.replace("latestStatus","checkpoint_status");
        }
        if(mileJsonStr.contains("locusDetailed")) {
            mileJsonStr = mileJsonStr.replace("locusDetailed","Details");
        }

        return mileJsonStr;
    }
}